package cn.iocoder.yudao.module.system.service.a_audio_and_video;

import cn.hutool.core.date.DateUtil;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
import cn.iocoder.yudao.module.system.config.a_gaojian.ConnectionConfig;
import cn.iocoder.yudao.module.system.config.tools.FFmpegTools;
import cn.iocoder.yudao.module.system.config.transcoder.CutterTask;
import cn.iocoder.yudao.module.system.config.transcoder.ExtractTask;
import cn.iocoder.yudao.module.system.config.transcoder.Transcoder;
import cn.iocoder.yudao.module.system.controller.admin.a_audio_and_video.vo.*;
import cn.iocoder.yudao.module.system.dal.dataobject.a_audio_and_video.AudioAndVideoDO;
import cn.iocoder.yudao.module.system.dal.dataobject.a_audio_and_video.AudioAndVideoDOReq;
import cn.iocoder.yudao.module.system.dal.mysql.a_audio_and_video.AudioAndVideoMapper;
import cn.iocoder.yudao.module.system.service.a_audio_and_video_log.AudioAndVideoLogService;
import cn.iocoder.yudao.module.system.service.hybase.HyBaseService;
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import cn.iocoder.yudao.module.system.util.a_gaojian.*;
import com.trs.hybase.client.*;
import com.trs.hybase.client.params.SearchParams;
import io.vavr.Tuple2;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.*;
import java.nio.channels.FileChannel;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;


/**
 * @author lxj
 */
@Service
@Validated
@Slf4j
public class AudioAndVideoServiceImpl implements AudioAndVideoService {

    @Resource
    AudioAndVideoLogService audioAndVideoLogService;

    @Autowired
    AdminUserService adminUserService;

    @Autowired
    AudioAndVideoMapper audioAndVideoMapper;

    @Resource
    Transcoder transcoder;

    @Resource
    ExtractTask extractTask;

    @Resource
    CutterTask cutterTask;

    @Resource
    FFmpegTools fFmpegTools;

    @Resource
    private HyBaseService hyBaseService;

    @Resource
    JdbcTemplate jdbcTemplate;

    @Resource
    private RestTemplate restTemplate;

    @Value("${file.audio.host}")
    private String AUDIO_HOST;

    @Value("${file.audio.path}")
    private String FILE_AUDIO;

    /**
     * 上传文件存储在本地的根路径 - 音频
     */
    @Value("${fileData.audioPath}")
    private String audioPath;
    /**
     * 上传文件存储在本地的根路径 - 视频
     */
    @Value("${fileData.videoPath}")
    private String videoPath;
    /**
     * 上传文件存储在本地的根路径 - 视频封面
     */
    @Value("${fileData.videoCoverPath}")
    private String videoCoverPath;
    /**
     * 删除文件存储在本地的根路径
     */
    @Value("${fileData.delPath}")
    private String delPath;
    /**
     * 图片表名
     */
    @Value("${hybase.dbname.audioAndVideo}")
    private String dbName;
    /**
     * ip
     */
    @Value("${ip}")
    private String ip ;

    /**
     * 资源库接口地址
     */
    @Value("${reslib.url}")
    private String reslibUrl;

    /**
     * 资源库token
     */
    @Value("${reslib.token}")
    private String reslibToken;

    @Override
    public PageResult<AudioAndVideoQukanPageReqVO> getPageByQukan(AudioAndVideoPageReqVO pageReqVO , Long userId) {
        PageResult<AudioAndVideoQukanPageReqVO> pageResult = new PageResult<>();
        List<AudioAndVideoDO> list = audioAndVideoMapper.selectList("creator", userId);

        List<AudioAndVideoQukanPageReqVO> pageQukanReqVOList = list.stream()
                .map(audioAndVideoDO -> {
                    AudioAndVideoQukanPageReqVO pageQukanReqVO = new AudioAndVideoQukanPageReqVO();
                    BeanUtils.copyProperties(audioAndVideoDO, pageQukanReqVO);
                    pageQukanReqVO.setId(audioAndVideoDO.getId().toString());
                    pageQukanReqVO.setUrl(ip + audioAndVideoDO.getAbsolutePath());
                    return pageQukanReqVO;
                })
                .collect(Collectors.toList());

        pageResult.setTotal((long) list.size());
        pageResult.setList(pageQukanReqVOList);

        return pageResult;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean delete(Long[] ids) {
        for (Long id : ids) {
            audioAndVideoMapper.deleteById(id);
            //同步删除海贝
            CompletableFuture future = CompletableFuture.runAsync(() -> {
                hyBaseService.executeDeleteQuery(dbName, id);
            });
        }
        return true;
    }

    @Override
    public Boolean updateReview(String id, Integer reviewStatus) {
        String sql = String.format("update gj_audio_and_video set review_status = %s where id = %s", reviewStatus, id);
        int row = jdbcTemplate.update(sql);
        return row > 0;
    }

    @Override
    public Boolean updateShare(String id, String shareStatus) {
        String sql = String.format("update gj_audio_and_video set is_share = %s where id = %s", shareStatus, id);
        int row = jdbcTemplate.update(sql);
        return row > 0;
    }

    @Override
    public Boolean updatePush(String id, String pushStatus) {
        AudioAndVideoDO audioAndVideoDO = get(id);

        String type = audioAndVideoDO.getType() == 0 ? "/audio" : "/video";

        // 获取文件并上传
        MultipartFile multipartFile;
        try {
            multipartFile = FileToMultipartFileUtils.convertFileToMultipartFile(new File(audioAndVideoDO.getUrl()));
        } catch (IOException e) {
            e.printStackTrace();
            log.info("文件 multipartFile 类型转换 File 类型失败，请检查文件是否正确！");
            return false;
        }
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);
        headers.set("tenant-id", "1");
        headers.set("Authorization", "Bearer " + reslibToken);

        MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
        body.add("file", multipartFile.getResource());
        body.add("name", audioAndVideoDO.getFileName());
        body.add("author", audioAndVideoDO.getCreateUserName());
        body.add("source", "融媒生产系统");

        // 上传文件
        org.springframework.http.HttpEntity<MultiValueMap<String, Object>> entity = new org.springframework.http.HttpEntity<>(body, headers);
        ResponseEntity<CommonResult> response = restTemplate.postForEntity(reslibUrl + type +"/upload/sava", entity, CommonResult.class);
        if (response.getBody().getCode() != 200) return false;

        audioAndVideoDO.setIsPush(pushStatus);
        int update = audioAndVideoMapper.updateById(audioAndVideoDO);

        // 异步同步数据到海贝
        CompletableFuture.runAsync(() -> {
            Map<String, Object> map = new HashMap<>();
            map.put("id", id);
            map.put("is_push", pushStatus);
            hyBaseService.updateHybaseById(dbName, Long.valueOf(id), map);
        });
        return update > 0;
    }



    @Override
    public AudioAndVideoDO get(String id) {
        return audioAndVideoMapper.selectById(id);
    }

    @Override
    public AudioAndVideoReqVO getByQukan(String id) {
        AudioAndVideoDO audioAndVideoDO = audioAndVideoMapper.selectById(id);
        AudioAndVideoReqVO reqVO = new AudioAndVideoReqVO();
        BeanUtils.copyProperties(audioAndVideoDO, reqVO);
        reqVO.setUrl(audioAndVideoDO.getUrl());
        reqVO.setVideoCoverUrl(audioAndVideoDO.getVideoCoverUrl());
        return reqVO;
    }

    @Override
    public Boolean update(AudioAndVideoDO audioAndVideoDO) {
        System.out.println("audioAndVideoDO"+audioAndVideoDO.toString());
        return audioAndVideoMapper.save(audioAndVideoDO);
    }

    @Override
    public long insertAudioAndVideoByDay() {
        //创建与服务器的连接实例
        TRSConnection conn = ConnectionConfig.getTRSConnection();
        SearchParams param = new SearchParams();
        TRSResultSet resultSet;
        String whereStr = "deleted:0 AND create_user_id:" + WebFrameworkUtils.getLoginUserId() + " AND create_time:[" + DateUtils.formatYYYYMMDDToStr(DateUtils.getDayStart(new Date())) + " TO " + DateUtils.formatYYYYMMDDToStr(DateUtils.getDayStart(new Date())) + "]";
        try {
            //在"pictures"数据库里检索
            resultSet = conn.executeSelect(dbName, whereStr, 0, 10000, param);
            if (resultSet != null) {
                return resultSet.getNumFound();
            }
        } catch (TRSException e) {
            System.out.println("ErrorCode:" + e.getErrorCode());
            System.out.println("ErrorString:" + e.getErrorString());
        } finally {
            if (conn != null) {
                conn.close();
            }
        }
        return 0;
    }

    @Override
    public Boolean updateQukanUrl(AudioAndVideoDO audioAndVideoDO) {
        AudioAndVideoDO avDO = audioAndVideoMapper.selectById(audioAndVideoDO.getId());
        avDO.setQukanUrl(audioAndVideoDO.getQukanUrl());
        avDO.setQukanVideoCoverUrl(audioAndVideoDO.getQukanVideoCoverUrl());
        int update = audioAndVideoMapper.updateById(avDO);
        log.info("updateQukanUrl 更新趣看url：{}", update);
        return update > 0;
    }

    @Override
    public Boolean uploadChunk(int chunkNumber, int chunkTotal, MultipartFile file, String fileMD5, String fileName) {
        String ext = fileName.substring(fileName.indexOf(".")+1);
        Integer type = getType(ext);
        String chunkPath = getFilePath( "/" + fileMD5, type);
        String chunkFile = chunkPath + "/" + fileMD5 + ".part" + chunkNumber;
        File chunkDir = new File(chunkPath);
        if(!chunkDir.exists()){
            chunkDir.mkdirs();
        }
        Boolean bool = false;
        try {
            file.transferTo(Paths.get(chunkFile));
            bool = true;
        } catch (IOException ioException) {
            throw new ServiceException(500, "系统错误!");
        }
        return bool;
    }

    @Override
    public String createFile(FileUploadNewReqVO uploadReqVO) {
        String address;
        String extension = VideoUtils.getExtension(uploadReqVO.getFile());
        System.out.println("文件后缀："+extension);
        Integer type = getType(extension);
        try {
            if(0==type){
                address = videoPath+VideoUtils.upload(videoPath, uploadReqVO.getFile());
            }else {
                address = audioPath+AudioUtils.upload(audioPath, uploadReqVO.getFile());
            }
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException();
        }
        return address;
    }

    @Override
    public String save(AudioAndVideoBaseVO baseVO) throws IOException {
//        Boolean idTranscoder = false; // 定义转码状态
//        if (baseVO.getId() == null || baseVO.getId() <= 0) {
//            idTranscoder = true;
//            baseVO.setTranscoderStatus(0);
//        } else {
//            if (!audioAndVideoMapper.selectById(baseVO.getId()).getUrl().equals(baseVO.getUrl())) {
//                idTranscoder = true;
//                //更换视频重新转码
//                baseVO.setTranscoderStatus(0);
//            }
//        }
        AudioAndVideoDO audioAndVideoDO = new AudioAndVideoDO();
        BeanUtils.copyProperties(baseVO, audioAndVideoDO);
//        audioAndVideoDO.setDuration(Double.valueOf(baseVO.getDuration()));
        System.out.println("转参："+audioAndVideoDO.toString());
        Boolean bool = audioAndVideoMapper.save(audioAndVideoDO);
//        if (bool && idTranscoder) {
//            log.info("执行音视频转码任务。。。");
//            transcoder.run(audioAndVideoDO);
//        }
//        if (audioAndVideoDO.getExtractStatus() == null || audioAndVideoDO.getExtractStatus() == 0) {
//            log.info("执行字幕提取任务。。。");
//            audioAndVideoDO.setExtractStatus(1);
//            extractTask.run(audioAndVideoDO);
//        }
        return audioAndVideoDO.getId().toString();
    }

    @Override
    public PageResult<AudioAndVideoDO> selectPage(AudioAndVideoPageReqVO pageReqVO) {
        return audioAndVideoMapper.page(pageReqVO);
    }

    @Override
    public Boolean audioCutter(Long id, String startTime, String duration) {
        // 获取音频文件记录
        AudioAndVideoDO avDO = audioAndVideoMapper.selectById(id);
        if (avDO == null) return false;  // 如果没有记录，返回false
        // 获取当前日期的路径部分 (年/月/日)
        String datePath = DateUtil.format(new Date(), "yyyy/MM/dd");
        // 构造存储路径
        String localPath = FILE_AUDIO + "/" + datePath;
        new File(localPath).mkdirs();  // 确保目录存在
        // 构造裁剪后文件的名称
        String cutterName = DateTimeUtils.getFileName() + "_cutter." + avDO.getExt();
        // 执行裁剪操作
//        cutterTask.run(avDO.getAbsolutePath(), startTime, duration, localPath + "/" + cutterName); // 取消使用异步执行
        Boolean bol = fFmpegTools.audioCutter(avDO.getAbsolutePath(), startTime, duration, localPath + "/" + cutterName);
        // 更新数据库中的文件路径
        String outputPath = AUDIO_HOST + "/" + datePath + "/" + cutterName;
        return bol && audioAndVideoMapper.updateAudioCutter(id, outputPath) > 0;
    }

    @Override
    public Boolean videoCutter(String input, String startTime, String duration) {
        String ext = FileUtil.getExt(input);
        String out = videoPath + "/" + DateTimeUtils.getFileName() + "_cutter." + ext;
        return fFmpegTools.videoCutter(input, startTime, duration, out);
    }

    @Override
    public Boolean updateDuration(Long id) {
        AudioAndVideoDO audioAndVideoDO = audioAndVideoMapper.selectById(id);
        Long duration = fFmpegTools.readVideo(audioAndVideoDO.getAudioCutter())._1;
        audioAndVideoDO.setDuration(String.valueOf(duration.doubleValue()));
        boolean update = audioAndVideoMapper.updateDuration(id, duration.doubleValue()) > 0;
        log.info("updateDuration 修改音频时长：{}", update);
        return update;
    }

    /**
     * 获取音频和视频的下载地址
     * @param id
     * @return
     */
    @Override
    public Object getAudioAndVideoDownloadUrl(String id) {
        AudioAndVideoDO audioAndVideoDO = audioAndVideoMapper.selectById(id);
        if (audioAndVideoDO != null) {
            String url = audioAndVideoDO.getUrl();
            return url;
        }else {
            return "未获取到下载地址";
        }
    }

    /**
     * 更新下载次数
     * @param id
     */
    @Override
    public void updateDownloadCount(String id) {
        audioAndVideoMapper.updateDownloadCount(id);
    }

    public AudioAndVideoDOReq upload(AudioAndVideoDOReq audioAndVideoDOReq) throws IOException {
        String path = audioAndVideoDOReq.getType() == 0 ? videoPath : audioPath;
        String address = audioAndVideoDOReq.getType() == 0
                ? VideoUtils.upload(videoPath, audioAndVideoDOReq.getFile())
                : AudioUtils.upload(audioPath, audioAndVideoDOReq.getFile());
        audioAndVideoDOReq.setUrl(path + address);
        return audioAndVideoDOReq;
    }

    public static String getFileName(){
        Random random = new Random();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 5; i++) {
            sb.append(random.nextInt(10));
        }
        Long time = new java.util.Date().getTime();
        return String.valueOf(time) + sb.toString();
    }

    private String getFilePath(String path,Integer type) {
        if(0 == type){
            return videoPath + path;
        }else {
            return audioPath + path;
        }
    }

    public static Integer getType(String prefix) {
        String[] types = {
                "3gp", "avi", "flv", "f4v", "gif", "m3u8", "m4v", "mkv", "mp4", "mpeg", "rm", "rmvb", "ts", "wmv"
        };
        List<String> list = Arrays.asList(types);
        if(list.contains(prefix)){
            return 0;
        }else {
            return 1;
        }
    }
}
